//
//  PopView.swift
//  UIViewAndCALayer
//
//  Created by fuyoufang on 2021/10/27.
//

import Foundation
import UIKit

// CAAnimation 的 delegate 的类型为 CAAnimationDelegate
// CALayer 的 delegate 的类型为 CALayerDelegate

private var currentAnimationContext: UnsafePointer<Void?>? = nil
private var popAnimationContext: Void?

extension UIView {
    fileprivate static var popAnimationState = [PopAnimationState]()
    
    static func popAnimation(duration: TimeInterval, animations: () -> Void) {
        withUnsafePointer(to: &popAnimationContext) { p in
            currentAnimationContext = p
        }
        
        animations()
    
        popAnimationState.forEach { state in
            let layer = state.layer
            let oldValue = state.oldValue
            let keyPath = state.keyPath
            guard let newValue = layer.value(forKey: keyPath) else {
                return
            }
            
            let easing: Float = 0.2
            let easeIn = CAMediaTimingFunction(controlPoints: 1, 0, (1 - easing), 1)
            let easeOut = CAMediaTimingFunction(controlPoints: easing, 0, 0, 1)
            
            let animation = CAKeyframeAnimation(keyPath: keyPath)
            animation.duration = 1
            animation.keyTimes = [0, 0.35, 1]
            animation.values = [oldValue, newValue, oldValue]
            animation.timingFunctions = [easeIn, easeOut]
            
            /// 测试发现：在 CATransaction 的 begin() 和 commit() 之间修改 setDisableActions(_: )  的话
            /// 不会影响到后继的值。
            /// 所有这里不用考虑恢复
            // debugPrint("begin: \(CATransaction.disableActions())")
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            layer.setValue(oldValue, forKey: keyPath)
            // debugPrint("in: \(CATransaction.disableActions())")
            CATransaction.commit()
            // debugPrint("end: \(CATransaction.disableActions())")
            
            layer.add(animation, forKey: nil)
        }
        
        popAnimationState.removeAll()
        currentAnimationContext = nil
    }
    
    struct PopAnimationState {
        let layer: CALayer
        let keyPath: String
        let oldValue: Any
    }
}

class PopView: UIView {
    override func action(for layer: CALayer, forKey event: String) -> CAAction? {
        
        var isInPop = false
        withUnsafePointer(to: &popAnimationContext) { p in
            isInPop = currentAnimationContext == p
        }
        
        if isInPop {
            if let oldValue = layer.value(forKey: event) {
                let state = PopAnimationState(layer: layer,
                                              keyPath: event,
                                              oldValue: oldValue)
                UIView.popAnimationState.append(state)
            }
        }
        
        return super.action(for: layer, forKey: event)
    }
    
}
